#include "ssBuffer.h"
#include "../ssAssert.h"
#include <cstdlib>

namespace StarSeeker
{

ssBuffer::ssBuffer()
:m_maxLen(0), m_len(0), m_data(0), m_memType(MEM_MGR_EXTERNAL)
{
}

ssBuffer::ssBuffer(ulint len)
	: m_maxLen(0), m_len(0), m_data(0), m_memType(MEM_MGR_EXTERNAL)
{
	Init(len);
}

ssBuffer::ssBuffer(const ssBuffer& bf)
: m_maxLen(0), m_len(0), m_data(0), m_memType(MEM_MGR_EXTERNAL)
{
	CopyFrom(bf);
}

ssBuffer::~ssBuffer()
{
	Clear();
}

void ssBuffer::operator=(const ssBuffer& bf)
{
	CopyFrom(bf);
}

bool ssBuffer::Init(ulint len, MEM_MGR_TYPE type)
{
	if (!m_data && len>0)
	{
		m_data = (char*)::operator new(len);
		m_maxLen = len;
		m_len = 0;
		m_memType = type;
		return true;
	}
	return false;
}

bool ssBuffer::Init(char* buf, ulint len)
{
	if (!m_data && buf && len>0)
	{
		m_data = buf;
		m_maxLen = len;
		m_len = 0;
		m_memType = MEM_MGR_EXTERNAL;
		return true;
	}
	return false;
}

bool ssBuffer::Resize(ulint newLen, bool saveData)
{
	if(m_memType==MEM_MGR_NEW && newLen>m_maxLen)
	{
		char* pData = (char*)::operator new(newLen);
		if (saveData && m_data && m_len>0 && m_len<newLen)
		{
			memcpy(pData, m_data, m_len);
		}
		else
		{
			m_len = 0;
		}
		SS_SAFE_DELETE(m_data);
		m_maxLen = newLen;
		m_data = pData;
		return true;
	}
	return false;
}

char* ssBuffer::Buf() const
{
	return m_data;
}

char* ssBuffer::CurrentBuf()
{
	if (m_len < m_maxLen)
		return m_data + m_len;
	else
		return 0;
}

StarSeeker::ulint ssBuffer::Size() const
{
	return m_len;
}

StarSeeker::ulint ssBuffer::ResidueSize()
{
	if (m_len < m_maxLen)
		return m_maxLen - m_len;
	else
		return 0;
}

StarSeeker::ulint ssBuffer::MaxSize() const
{
	return m_maxLen;
}

bool ssBuffer::IsFull() const
{
	return m_len >= m_maxLen;
}

bool ssBuffer::AddSize(ulint sz)
{
	if (m_len + sz <= m_maxLen)
	{
		m_len += sz;
		return true;
	}
	return false;
}

bool ssBuffer::SubSize(ulint sz)
{
	if (sz <= m_len)
	{
		m_len -= sz;
		return true;
	}
	return false;
}

bool ssBuffer::SetSize(ulint sz)
{
	if (sz <= m_maxLen)
	{
		m_len = sz;
		return true;
	}
	return false;
}

void ssBuffer::Empty(bool setZero)
{
	m_len = 0;
	if(setZero && m_data && m_maxLen)
		memset(m_data, 0, m_maxLen);
}

void ssBuffer::Clear()
{
	if(m_memType>MEM_MGR_EXTERNAL)
	{
		SS_SAFE_DELETE(m_data)
	}
	m_maxLen = 0;
	m_len = 0;
	m_data = 0;
	m_memType = MEM_MGR_EXTERNAL;
}

StarSeeker::MEM_MGR_TYPE ssBuffer::MemType() const
{
	return m_memType;
}

bool ssBuffer::Write(const void* buf, ulint len)
{
	if (len>0 && len + m_len <= m_maxLen)
	{
		memmove(CurrentBuf(), buf, len);
		AddSize(len);
		return true;
	}
	else if (m_memType==MEM_MGR_NEW && Resize((m_maxLen + len) * 2, true))
	{
		memcpy(CurrentBuf(), buf, len);
		AddSize(len);
		return true;
	}
	SS_ASSERT_DEBUG(false);
	return false;
}

bool ssBuffer::SubData(ulint offset, ulint len)
{
	if (m_data && m_len>0)
	{
		if (offset==0)
		{
			m_len = len;
			return true;
		}
		else if (offset + len <= m_len)
		{
			memmove(m_data, m_data + offset, len);
			m_len = len;
			return true;
		}
	}
	return false;
}

bool ssBuffer::MoveForward(ulint offset)
{
	if (offset == m_len)
	{
		Empty(false);
		return true;
	}
	if (offset>0 && offset < m_len)
		return SubData(offset, m_len - offset);
	else
		return false;
}

void ssBuffer::ClearExcludeMomery()
{
	m_maxLen = 0;
	m_len = 0;
	m_data = 0;
	m_memType = MEM_MGR_EXTERNAL;
}

void ssBuffer::CopyFrom(const ssBuffer& bf)
{
	Clear();
	if (bf.m_memType>MEM_MGR_EXTERNAL)
	{
		m_memType = bf.m_memType;
		m_len = bf.m_len;
		m_maxLen = bf.m_maxLen;
		if (m_len <= m_maxLen)
		{
			m_data = (char*)::operator new(m_maxLen);
			if (m_len > 0)
			{
				::memmove(m_data, bf.m_data, m_len);
			}
		}
	}
	else
	{
		m_memType = MEM_MGR_EXTERNAL;
		m_len = bf.m_len;
		m_maxLen = bf.m_maxLen;
		m_data = bf.m_data;
	}
}

}
